// Copyright (c) 2021 Presto Labs Pte. Ltd.
// Author: leon

#pragma once

#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include <boost/asio/dispatch.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "coin2/base/request/session_pool.h"
#include "coin2/base/conversion.h"
#include "coin2/base/string_util.h"
#include "coin2/exchange/base/feed/symbol_updater.h"
#include "coin2/exchange/base/api_util/raw_subscriber_util.h"
#include "coin2/exchange/deribit_options/symbology/product.h"
#include "coin2/exchange/util/json_util.h"

namespace coin2::exchange::deribit_options::feed_v2 {
namespace impl {
using coin2::base::request::GetHttpsContent;

struct ExtractedOptionsInfo {
  std::string strike_price;
  std::string instrument_name;
};

class DeribitOptionsSymbolUpdater : public base::feed::SymbolUpdater {
 public:
  explicit DeribitOptionsSymbolUpdater(const MarketExchangeApi& mea)
      : SymbolUpdater(mea) {
    using std::string_literals::operator""s;
    for (const std::string& currency : {"BTC"s, "ETH"s}) {
      market_info_str_ = GetHttpsContent(
          "www.deribit.com",
          "/api/v2/public/get_instruments?currency=" + currency + "&kind=option");
      rapidjson::Document document;
      document.Parse<rapidjson::kParseNumbersAsStringsFlag>(market_info_str_.data());
      if (document.HasParseError()) {
        throw std::runtime_error(
            std::string("JSON parse error: ") + GetParseError_En(document.GetParseError()));
      }
      query_ts_ = stoll(document["usOut"].GetString()) * 1e3;
      for (const auto& instrument_json : document["result"].GetArray()) {
        extracted_options_infos_.push_back(ExtractedOptionsInfo{
            std::to_string(static_cast<int>(stod(instrument_json["strike"].GetString()))),
            instrument_json["instrument_name"].GetString()
        });
      }
    }
  }
  std::vector<std::pair<std::string, std::string>> GetUpdatedNormSymbols(
      const std::string& norm_template) override {
    std::vector<std::pair<std::string, std::string>> to_update;
    if (StringEndsWith(norm_template, ".ALL")) {
      for (const auto& info : extracted_options_infos_) {
        std::string norm_replace_price = norm_template;
        norm_replace_price = replace_string(norm_replace_price, "ALL", info.strike_price);
        std::string native = symbology::DeribitOptionsProduct::FromNormStr(
            norm_replace_price, query_ts_).native_subscription_symbol();
        if (native == info.instrument_name) {
          to_update.push_back({norm_replace_price, native});
        }
      }
    } else {
      std::string native = symbology::DeribitOptionsProduct::FromNormStr(
          norm_template, query_ts_).native_subscription_symbol();
      to_update.push_back({norm_template, native});
    }
    return to_update;
  }

 private:
  std::string market_info_str_;
  std::vector<ExtractedOptionsInfo> extracted_options_infos_;
  std::set<std::string> native_currencies_;
  int64_t query_ts_;
};
}  // namespace impl
using impl::DeribitOptionsSymbolUpdater;
}  // namespace coin2::exchange::deribit_options::feed_v2
